#pragma once
#include <vector>
#include <unordered_map>
#include <functional>
#include <sys/select.h>
#include "tcp_socket.hpp"

inline void PrintFdSet(fd_set *fds, int max_fd)
{
    printf("select fd:");
    for (int i = 0; i < max_fd + 1; i++)
    {
        if (!FD_ISSET(i, fds))
        {
            continue;
        }
        printf("%d ", i);
    }
    printf("\n");
}

typedef std::function<void(const std::string &, std::string *)> Handler;

class Selector
{
public:
    Selector()
    {
        max_fd_ = 0;
        FD_ZERO(&read_fds_);
    }
    bool Add(TcpSocket &sock)
    {
        int fd = sock.GetFd();
        printf("[Selector::Add]%d\n", fd);
        if (fd_map_.find(fd) != fd_map_.end())
        {
            printf("Add failed!fd has already in select!\n");
            return false;
        }
        fd_map_[fd] = sock;
        FD_SET(fd, &read_fds_);
        if (fd > max_fd_)
        {
            max_fd_ = fd;
        }
        return true;
    }
    bool Del(TcpSocket &sock)
    {
        int fd = sock.GetFd();
        printf("[Selector::Del]%d\n", fd);
        if (fd_map_.find(fd) == fd_map_.end())
        {
            printf("Del failed!fd has not in select!\n");
            return false;
        }
        fd_map_.erase(fd);
        FD_CLR(fd, &read_fds_);
        for (int i = max_fd_; i >= 0; i--)
        {
            if (FD_ISSET(i, &read_fds_))
            {
                max_fd_ = i;
                break;
            }
        }
        return true;
    }
    bool Wait(std::vector<TcpSocket> *output)
    {
        output->clear();
        fd_set tmp = read_fds_;
        PrintFdSet(&tmp, max_fd_);
        int nfds = select(max_fd_ + 1, &tmp, NULL, NULL, NULL);
        if (nfds < 0)
        {
            perror("select_failed");
            return false;
        }
        for (int i = 0; i < max_fd_ + 1; i++)
        {
            if (!FD_ISSET(i, &tmp))
            {
                continue;
            }
            output->push_back(fd_map_[i]);
        }
        return true;
    }

private:
    fd_set read_fds_;
    int max_fd_;
    std::unordered_map<int, TcpSocket> fd_map_;
};
class TcpSelectServer
{
public:
    TcpSelectServer()
    {
    }
    bool Start(Handler handler) const
    {
        TcpSocket listen_sock;
        bool ret = listen_sock.Socket();
        if (!ret)
        {
            return false;
        }
        ret = listen_sock.Bind();
        if (!ret)
        {
            return false;
        }
        ret = listen_sock.Listen(5);
        if (!ret)
        {
            return false;
        }
        // 创建 selector对象
        Selector selector;
        selector.Add(listen_sock);
        while (1)
        {
            std::vector<TcpSocket> output;
            int ret = selector.Wait(&output);
            printf("flag1 we are waiting select %d\n",ret);
            if (!ret)
            {
                continue;
            }
            // 根据就绪文件描述符的差别，决定后续的处理逻辑
            for (ssize_t i = 0; i < output.size(); i++)
            {
                printf("output fd is %d,listen fd is %d\n",output[i].GetFd(), listen_sock.GetFd());
                if (output[i].GetFd() == listen_sock.GetFd())
                {
                    // 如果就绪的文件描述符是 listen_sock, 就执行 accept, 并加入到 select 中
                    TcpSocket new_sock;

                    if (!listen_sock.Accept(&new_sock,NULL, NULL))
                    {
                        continue;
                    }
                    selector.Add(new_sock);
                    printf("flag3 :is add&accpet?\n");
                }
                else
                {
                    // 如果就绪的文件描述符是 new_sock, 就进行一次请求的处理
                    printf("flag2 :is in else?\n");
                    std::string req, resp;
                    bool ret = output[i].Recv(&req);
                    if (!ret)
                    {
                        selector.Del(output[i]);
                        // [注意!] 需要关闭 socket
                        output[i].Close();
                        continue;
                    }
                    handler(req, &resp);
                    printf("after handler resp is %s\n",resp.c_str());
                    output[i].Send(resp);
                }
            }
        }
        return true;
    }

private:
    std::string ip_;
    uint16_t port_;
};